package uu.game.majiang.core;

import java.util.ArrayList;

import uu.game.majiang.core.Pai.Chifa;
import uu.game.majiang.core.Pai.Gangfa;
import uu.game.majiang.core.Pai.Pengfa;

/**
 * 游戏者
 * 
 * @author uu
 * @version 2010-7-8 下午05:23:38
 */
public abstract class AbstractPlayer {
    
    private static final char    SPACE = ' ';
    private static final char    LEFT  = '【';
    private static final char    RIGHT = '】';
    
    /**
     * 游戏者名字
     */
    protected String             name;
    
    /**
     * 所在的盘
     */
    protected Pan                pan;
    
    /**
     * 门风(当前盘)
     */
    protected Feng               menfeng;
    
    /**
     * 手里的牌(当前盘)
     */
    protected Pai                pai;
    
    /**
     * 出了的牌(当前盘)
     */
    protected ArrayList<Integer> chule = new ArrayList<Integer>(24);
    
    /**
     * @param name
     *            游戏者名字
     */
    protected AbstractPlayer(String name) {
        this.name = name;
    }
    
    /**
     * 摸牌。
     * 
     * @return 摸到的牌号，返回0表示当前盘已结束。
     */
    public int mo() {
        if (pan.isFinished())
            return 0;
        int paihao = pan.fapai();
        pai.mo(paihao);
        return paihao;
    }
    
    /**
     * 出牌
     * 
     * @param paihao
     *            出的牌的牌号
     */
    public void chu(int paihao) {
        pai.chu(paihao);
        chule.add(paihao);
    }
    
    /**
     * 出牌。根据牌在手里的牌中的索引号(从0开始，不包括吃碰杠的牌)出牌
     * 
     * @param index
     *            出的牌在手里的牌中的索引号(从0开始，不包括吃碰杠的牌)
     * @return 出的牌的牌号
     */
    public int chuByIndex(int index) {
        int paihao = pai.chuByIndex(index);
        chule.add(paihao);
        return paihao;
    }
    
    /**
     * 游戏者名字
     * 
     * @return 游戏者名字
     */
    public String getName() {
        return name;
    }
    
    /**
     * 设置游戏者名字
     * 
     * @param name
     *            游戏者名字
     */
    public void setName(String name) {
        this.name = name;
    }
    
    /**
     * 当前盘中手里的牌
     * 
     * @return 当前盘中手里的牌
     */
    public Pai getPai() {
        return pai;
    }
    
    /**
     * 当前盘中出过的牌
     * 
     * @return 当前盘中出过的牌
     */
    public ArrayList<Integer> getChule() {
        return chule;
    }
    
    /**
     * 当前盘的门风
     * 
     * @return 当前盘的门风
     */
    public Feng getMenfeng() {
        return menfeng;
    }
    
    /**
     * 别的玩家出牌时，此玩家可以进行的操作，可能是和牌、吃(最多3种)、碰(1种)、明杠(1种)
     * 
     * @param other
     *            别的玩家
     * @param paihao
     *            要操作的牌
     * @return 操作
     */
    public Operation[] can(AbstractPlayer other, int paihao) {
        if (other == this)
            return can();
        int n = 0;
        RelativeFangwei who = menfeng.getRelative(other.menfeng);
        boolean canHu = pai.canHu(who, paihao);
        if (canHu)
            n++;
        Pengfa pengfa = pai.canPeng(who, paihao);
        if (pengfa != null)
            n++;
        Gangfa gangfa = pai.canMinggangOther(who, paihao);
        if (gangfa != null)
            n++;
        Chifa[] chifas = new Chifa[0];
        if (who == RelativeFangwei.SHANGJIA)
            chifas = pai.canChi(paihao);
        n += chifas.length;
        if (n == 0)
            return new Operation[0];
        Operation[] ao = new Operation[n];
        n = 0;
        if (canHu)
            ao[n++] = new HuOperation(who, paihao);
        for (Chifa chifa : chifas)
            ao[n++] = new ChiOperation(chifa);
        if (pengfa != null)
            ao[n++] = new PengOperation(pengfa);
        if (gangfa != null)
            ao[n++] = new GangOperation(gangfa);
        return ao;
    }
    
    /**
     * 摸到牌时，此玩家可以进行的操作，可能是和牌、暗杠(最多3种)、明杠碰过的牌(最多3种)、补花
     * 
     * @return 操作
     */
    public Operation[] can() {
        int n = 0;
        boolean canHu = pai.canHu();
        if (canHu)
            n++;
        boolean canBuhua = pai.canBuhua();
        if (canBuhua)
            n++;
        Gangfa[] gangfasPeng = pai.canMinggangPeng();
        n += gangfasPeng.length;
        Gangfa[] gangfasAn = pai.canAngang();
        n += gangfasAn.length;
        if (n == 0)
            return new Operation[0];
        Operation[] ao = new Operation[n];
        n = 0;
        if (canHu)
            ao[n++] = new HuOperation();
        if (canBuhua)
            ao[n++] = new BuhuaOperation();
        for (Gangfa gangfa : gangfasPeng)
            ao[n++] = new GangOperation(gangfa);
        for (Gangfa gangfa : gangfasAn)
            ao[n++] = new GangOperation(gangfa);
        return ao;
    }
    
    /**
     * 上家出牌时，此玩家可以进行的吃牌操作，最多可能有3种
     * 
     * @param paihao
     *            要吃的牌
     * @return 吃牌操作
     */
    public ChiOperation[] canChi(int paihao) {
        Chifa[] cfs = pai.canChi(paihao);
        if (cfs.length == 0)
            return new ChiOperation[0];
        ChiOperation[] cos = new ChiOperation[cfs.length];
        int i = 0;
        for (Chifa cf : cfs)
            cos[i++] = new ChiOperation(cf);
        return cos;
    }
    
    /**
     * 别的玩家出牌时，此玩家可以进行的碰牌操作
     * 
     * @param player
     *            碰谁的牌
     * @param paihao
     *            要碰的牌
     * @return 碰牌操作
     */
    public PengOperation canPeng(AbstractPlayer player, int paihao) {
        RelativeFangwei who = menfeng.getRelative(player.menfeng);
        Pengfa pf = pai.canPeng(who, paihao);
        if (pf == null)
            return null;
        return new PengOperation(pf);
    }
    
    /**
     * 别的玩家出牌时，此玩家可以进行的杠牌操作，明杠
     * 
     * @param player
     *            杠谁的牌
     * @param paihao
     *            要杠的牌
     * @return 杠牌操作
     */
    public GangOperation canGang(AbstractPlayer player, int paihao) {
        RelativeFangwei who = menfeng.getRelative(player.menfeng);
        Gangfa gf = pai.canMinggangOther(who, paihao);
        if (gf == null)
            return null;
        return new GangOperation(gf);
    }
    
    /**
     * 摸到牌时，可以进行的杠牌操作，暗杠或者明杠碰过的牌，最多3种
     * 
     * @return 杠牌操作
     */
    public GangOperation[] canGang() {
        Gangfa[] gfsA = pai.canAngang();
        Gangfa[] gfsP = pai.canMinggangPeng();
        int a = gfsA.length;
        int p = gfsP.length;
        int n = a + p;
        if (n == 0)
            return new GangOperation[0];
        GangOperation[] gos = new GangOperation[n];
        int i = 0;
        for (Gangfa gf : gfsA)
            gos[i++] = new GangOperation(gf);
        for (Gangfa gf : gfsP)
            gos[i++] = new GangOperation(gf);
        return gos;
    }
    
    /**
     * 是否能和别人的牌
     * 
     * @param player
     *            谁的牌
     * @param paihao
     *            要判断的牌号
     * @return 和牌操作，如果不能和，则返回null
     */
    public HuOperation canHu(AbstractPlayer player, int paihao) {
        RelativeFangwei who = menfeng.getRelative(player.menfeng);
        if (!pai.canHu(who, paihao))
            return null;
        return new HuOperation(who, paihao);
    }
    
    /**
     * 是否能和自己摸到的牌
     * 
     * @return 和牌操作，如果不能和，则返回null
     */
    public HuOperation canHu() {
        if (!pai.canHu())
            return null;
        return new HuOperation();
    }
    
    /**
     * 是否能补花
     * 
     * @return 补花操作，如果不能补，则返回null
     */
    public BuhuaOperation canBuhua() {
        if (!pai.canBuhua())
            return null;
        return new BuhuaOperation();
    }
    
    /*
     * 当前所在的盘
     */
    Pan getPan() {
        return pan;
    }
    
    /*
     * 设置门风
     */
    void setMenfeng(Feng menfeng) {
        this.menfeng = menfeng;
    }
    
    /*
     * 设置所在的盘
     */
    void setPan(Pan pan) {
        this.pan = pan;
    }
    
    /*
     * 设置牌
     */
    void initPai(int[] pai13, Feng quanfeng) {
        pai = new Pai(pai13, quanfeng.getPaihao(), menfeng.getPaihao());
    }
    
    /*
     * 重置状态
     */
    void reset() {
        pan = null;
        pai = null;
    }
    
    private static String toStringChiPengGangOperation(Operation cpgOperation) {
        StringBuilder sb = new StringBuilder();
        sb.append(SPACE).append(LEFT).append(cpgOperation.who());
        int[] pais = cpgOperation.getPais();
        for (int j = 0; j < pais.length - 1; j++)
            sb.append(Pais.getPaiName(pais[j])).append(SPACE);
        sb.append(Pais.getPaiName(pais[pais.length - 1])).append(RIGHT);
        return sb.toString();
    }
    
    /**
     * 和牌操作
     */
    public class HuOperation implements Operation {
        
        private RelativeFangwei who;
        private int             paihao;
        
        private HuOperation(RelativeFangwei who, int paihao) {
            this.who = who;
            this.paihao = paihao;
        }
        
        private HuOperation() {
            this(RelativeFangwei.ZIJI, pai.getModao());
        }
        
        @Override
        public int getType() {
            return TYPE_HU;
        }
        
        @Override
        public int[] getPais() {
            return new int[] { paihao };
        }
        
        @Override
        public void execute() {
            pai.hu();
            pan.setHuPlayer(AbstractPlayer.this);
            pan.setHuPai(pai);
        }
        
        @Override
        public RelativeFangwei who() {
            return who;
        }
        
        @Override
        public String toString() {
            return " 和牌(H) " + who() + Pais.getPaiName(paihao);
        }
    }
    
    /**
     * 吃牌操作
     */
    public class ChiOperation implements Operation {
        
        private Chifa chifa;
        
        private ChiOperation(Chifa chifa) {
            this.chifa = chifa;
        }
        
        @Override
        public int getType() {
            return TYPE_CHI;
        }
        
        @Override
        public void execute() {
            pai.chi(chifa);
        }
        
        @Override
        public int[] getPais() {
            return chifa.getPais();
        }
        
        @Override
        public RelativeFangwei who() {
            return RelativeFangwei.SHANGJIA;
        }
        
        @Override
        public String toString() {
            return toStringChiPengGangOperation(this);
        }
    }
    
    /**
     * 碰牌操作
     */
    public class PengOperation implements Operation {
        
        private Pengfa pengfa;
        
        private PengOperation(Pengfa pengfa) {
            this.pengfa = pengfa;
        }
        
        @Override
        public int getType() {
            return TYPE_PENG;
        }
        
        @Override
        public void execute() {
            pai.peng(pengfa);
        }
        
        @Override
        public int[] getPais() {
            int paihao = pengfa.getPaihao();
            return new int[] { paihao, paihao, paihao };
        }
        
        @Override
        public RelativeFangwei who() {
            return pengfa.who();
        }
        
        @Override
        public String toString() {
            return toStringChiPengGangOperation(this);
        }
    }
    
    /**
     * 杠牌操作
     */
    public class GangOperation implements Operation {
        
        private Gangfa gangfa;
        
        private GangOperation(Gangfa gangfa) {
            this.gangfa = gangfa;
        }
        
        @Override
        public int getType() {
            return TYPE_GANG;
        }
        
        @Override
        public void execute() {
            pai.gang(gangfa);
            pai.mo(pan.bupai());
        }
        
        @Override
        public int[] getPais() {
            int paihao = gangfa.getPaihao();
            return new int[] { paihao, paihao, paihao, paihao };
        }
        
        @Override
        public RelativeFangwei who() {
            return gangfa.who();
        }
        
        @Override
        public String toString() {
            return toStringChiPengGangOperation(this);
        }
    }
    
    /**
     * 补花操作
     */
    public class BuhuaOperation implements Operation {
        
        @Override
        public int[] getPais() {
            return new int[0];
        }
        
        @Override
        public int getType() {
            return TYPE_BUHUA;
        }
        
        @Override
        public void execute() {
            pai.buhua();
            pai.mo(pan.bupai());
        }
        
        @Override
        public RelativeFangwei who() {
            return RelativeFangwei.ZIJI;
        }
        
        @Override
        public String toString() {
            return " 补花(B)";
        }
    }
}
